using System;
using System.Drawing;
using System.Net;
using System.Collections.Generic;
using System.Xml;
using System.Reflection;

namespace Bespoke.Common
{    
	/// <summary>
	/// Xml helper methods
	/// </summary>
	public static class XmlHelper
	{
		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="xmlNode"></param>
		/// <param name="attributeName"></param>
		/// <returns></returns>
		public static T GetAttribute<T>(XmlNode xmlNode, string attributeName)
		{
			return GetAttribute<T>(xmlNode, attributeName, true, default(T));
		}

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="xmlNode"></param>
		/// <param name="attributeName"></param>
		/// <param name="required"></param>
		/// <param name="defaultValue"></param>
		/// <returns></returns>
		public static T GetAttribute<T>(XmlNode xmlNode, string attributeName, bool required, T defaultValue)
		{
			T value;

			XmlNode foundNode = xmlNode.Attributes.GetNamedItem(attributeName);
			if (foundNode == null)
			{
				if (required)
				{
					throw new Exception("Required attribute [" + attributeName + "] not found");
				}
				else
				{
					value = defaultValue;
				}
			}
			else
			{
				object valueObject;
				Type type = typeof(T);
				string stringValue = foundNode.Value.Trim();

				if (type.IsEnum)
				{
					value = (T)Enum.Parse(type, stringValue, true);
				}
				else
				{
					switch (type.Name)
					{
                        case "Byte":
                            valueObject = byte.Parse(stringValue);
                            break;

						case "Int32":
							valueObject = int.Parse(stringValue);
							break;

						case "Single":
							valueObject = float.Parse(stringValue);
							break;

						case "Double":
							valueObject = double.Parse(stringValue);
							break;

						case "Boolean":
							valueObject = bool.Parse(stringValue);
							break;

                        case "Color":
                            valueObject = ParseColor(stringValue);
                            break;

						case "IPAddress":
							valueObject = IPAddress.Parse(stringValue);
							break;

						case "Point":
							valueObject = ParsePoint(stringValue);
							break;

						default:
							valueObject = stringValue;
							break;
					}

					value = (T)valueObject;
				}
			}

			return value;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="T"></typeparam>
		/// <param name="value"></param>
		/// <returns></returns>
		public static string ToString<T>(T value)
		{
			object valueObject = value;

			Type type = typeof(T);
			string stringValue;

			if (type.IsEnum)
			{
				stringValue = valueObject.ToString();
			}
			else
			{
				switch (type.Name)
				{
					case "Color":
						{
							Color color = (Color)valueObject;
							stringValue = string.Format("{0}, {1}, {2}, {3}", color.R, color.G, color.B, color.A);
							break;
						}

					case "Rectangle":
						{
							Rectangle rectangle = (Rectangle)valueObject;
							stringValue = string.Format("{0}, {1}, {2}, {3}", rectangle.X, rectangle.Y, rectangle.Width, rectangle.Height);
							break;
						}

					default:
						stringValue = valueObject.ToString();
						break;
				}
			}

			return stringValue;
		}

        /// <summary>
        /// 
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        private static Color ParseColor(string value)
        {
            try
            {
                MemberInfo[] colorMemberInfoList = typeof(Color).GetMember(value);
                if (colorMemberInfoList.Length != 1)
                {
                    throw new Exception("Could not find color value: " + value);
                }

                return (Color)typeof(Color).InvokeMember(colorMemberInfoList[0].Name, BindingFlags.GetProperty, null, null, null);
            }
            catch (Exception ex)
            {
                throw new InvalidCastException("Could not parse color", ex);
            }
        }

		/// <summary>
		/// 
		/// </summary>
		/// <param name="value"></param>
		/// <returns></returns>
		private static Point ParsePoint(string value)
		{
			string[] values = value.Split(DELIMITERS);
			if (values.Length != 2)
			{
				throw new InvalidCastException("Could not parse point");
			}

			try
			{
				int x = int.Parse(values[0].Trim());
				int y = int.Parse(values[1].Trim());

				return new Point(x, y);
			}
			catch (Exception ex)
			{
				throw new InvalidCastException("Could not parse point", ex);
			}
		}

		private static readonly char[] DELIMITERS = ",".ToCharArray();
	}
}
